//-----------------------------------------------------------------------------
//
//      Классы представителей(делегатов) клиентов на сервере
//      (c) РГУПС, ВЖД 30/11/2017
//      Разработал: Ковшиков С. В.
//
//-----------------------------------------------------------------------------
/*!
 *  \file
 *  \brief Классы представителей(делегатов) клиентов на сервере
 *  \copyright РГУПС, ВЖД
 *  \author Ковшиков С. В
 *  \date 30/11/2017
 */

#include "client-delegates.h"

#include <QTcpSocket>

#include "abstract-data-engine.h"



/******************************************************************************
 *  Данный класс предоставляет только тут часть публичного интерфейса делегата,
 *  которую можно использовать в основном приложении. Остальная часть
 *  публичного интерфейса делегата предназначена только для использования в
 *  пределах класса TcpServer. Причина - последовательный Tcp-сокет.
 *****************************************************************************/

ClientFace::ClientFace(AbstractClientDelegate *delegate, QObject *parent)
    : QObject(parent)
    , delegate_(delegate)
{

}




QString ClientFace::getName() const
{
    return delegate_->getName();
}




quintptr ClientFace::getId() const
{
    return delegate_->getId();
}




QByteArray ClientFace::getInputBuffer() const
{
    return delegate_->getInputBuffer();
}




void ClientFace::setOutputBuffer(QByteArray arr)
{
    delegate_->setOutputBuffer(arr);
}




/******************************************************************************
 *  Данный класс является базовым классом для реально используемых делегатов и
 *  содержит общий функционал и интерфейс.
 *****************************************************************************/
//-----------------------------------------------------------------------------
// Конструктор
//-----------------------------------------------------------------------------
AbstractClientDelegate::AbstractClientDelegate(QObject *parent)
    : QObject(parent)
    , face_(new ClientFace(this, this))
    , name_("")
    , localId_(0)
    , socket_(Q_NULLPTR)
    , engine_(Q_NULLPTR)
{

}



//-----------------------------------------------------------------------------
// Деструктор
//-----------------------------------------------------------------------------
AbstractClientDelegate::~AbstractClientDelegate()
{
    if (engine_)
        delete engine_;

    if (face_)
        delete face_;
}




//-----------------------------------------------------------------------------
// Вернуть указатель на доступную часть интерфейса
//-----------------------------------------------------------------------------
ClientFace *AbstractClientDelegate::face()
{
    return face_;
}



//-----------------------------------------------------------------------------
// Вернуть имя
//-----------------------------------------------------------------------------
QString AbstractClientDelegate::getName() const
{
    return name_;
}



//-----------------------------------------------------------------------------
// Установить имя
//-----------------------------------------------------------------------------
void AbstractClientDelegate::rememberName()
{
    if (!socket_->isOpen())
        return;

    // Читаем остатки данных из сокета
    name_ = socket_->readAll();
}




void AbstractClientDelegate::forgetName()
{
    name_.clear();
}



//-----------------------------------------------------------------------------
// Установить сокет
//-----------------------------------------------------------------------------
void AbstractClientDelegate::setSocket(QTcpSocket *sock)
{
    socket_ = sock;
    localId_ = sock->socketDescriptor();
}



//-----------------------------------------------------------------------------
// Вернуть дескриптор сокета
//-----------------------------------------------------------------------------
qintptr AbstractClientDelegate::getId() const
{
    return localId_;
}



//-----------------------------------------------------------------------------
// Установить механизм подготовки данных
//-----------------------------------------------------------------------------
void AbstractClientDelegate::setDataEngine(AbstractDataEngine *engine)
{
    if (engine == Q_NULLPTR)
        return;

    if (engine_)
        delete engine_;

    engine_ = engine;
}



//-----------------------------------------------------------------------------
// Вернуть буффер данных принятых от клиента
//-----------------------------------------------------------------------------
QByteArray AbstractClientDelegate::getInputBuffer() const
{
    return engine_->getInputBuffer();
}




/******************************************************************************
 *  Класс делегата-пустышки, предназначенный для предотвращения исключений,
 *  связанных с Q_NULLPTR. Данный класс выступает чем-то вроде null-object'а и
 *  безмолвно "впитывает" вызовы всех методов.
 *****************************************************************************/
//-----------------------------------------------------------------------------
// Конструктор
//-----------------------------------------------------------------------------
DummyDelegate::DummyDelegate(QObject* parent)
    : AbstractClientDelegate(parent)
{
    name_ = "dummy";
    engine_ = new NullDataEngine();
}

//-----------------------------------------------------------------------------
// Деструктор
//-----------------------------------------------------------------------------
DummyDelegate::~DummyDelegate()
{
    // Ничего не делать
}

void DummyDelegate::rememberName()
{
    // Ничего не делать
}

//-----------------------------------------------------------------------------
// Установить имя (пустышка)
//-----------------------------------------------------------------------------
//void DummyDelegate::setName(QString name)
//{
//    Q_UNUSED(name);
//    // Ничего не делать
//}

//-----------------------------------------------------------------------------
// Установить сокет (пустышка)
//-----------------------------------------------------------------------------
void DummyDelegate::setSocket(QTcpSocket *sock)
{
    Q_UNUSED(sock);
    // Ничего не делать
}


//-----------------------------------------------------------------------------
// Установить механизм подготовки данных (пустышка)
//-----------------------------------------------------------------------------
void DummyDelegate::setDataEngine(AbstractDataEngine *engine)
{
    Q_UNUSED(engine);
    // Ничего не делать
}

//-----------------------------------------------------------------------------
// Сохранить буффер запроса от клиента (пустышка)
//-----------------------------------------------------------------------------
void DummyDelegate::storeInputData()
{
    // Ничего не делать
}

//-----------------------------------------------------------------------------
// Установить буффер данных для отправки клиенту (пустышка)
//-----------------------------------------------------------------------------
void DummyDelegate::setOutputBuffer(QByteArray buf)
{
    Q_UNUSED(buf)
    // Ничего не делать
}

//-----------------------------------------------------------------------------
// Отправить результат авторизации (пустышка)
//-----------------------------------------------------------------------------
void DummyDelegate::sendAuthorizationResponse(ATcp::AuthResponse resp)
{
    Q_UNUSED(resp);
    // Ничего не делать
}

//-----------------------------------------------------------------------------
// Отправить данные клиенту (пустышка)
//-----------------------------------------------------------------------------
void DummyDelegate::sendDataToTcpClient()
{
    // Ничего не делать
}




/*
 *  Данный класс является реализацией реального делегата Tcp клиента. Большая
 *  часть публичного интерфейса предназначена для использования только в
 *  пределах класса TcpServer. Остальная часть интерфейса предоставляется
 *  посредством классом ClientFace.
 */
//-----------------------------------------------------------------------------
// Конструктор
//-----------------------------------------------------------------------------
ClientDelegate::ClientDelegate(QObject* parent)
    : AbstractClientDelegate(parent)
{

}



//-----------------------------------------------------------------------------
// Деструктор
//-----------------------------------------------------------------------------
ClientDelegate::~ClientDelegate()
{

}



//-----------------------------------------------------------------------------
// Сохранить буффер запроса от клиента
//-----------------------------------------------------------------------------
void ClientDelegate::storeInputData()
{
    if (!socket_->isOpen())
        return;

    // Читаем остатки данных из сокета
    engine_->setInputBuffer(socket_->readAll());
    // Оповещаем о приёме данных
    emit face_->dataReceived(engine_->getInputBuffer());
}



//-----------------------------------------------------------------------------
// Установить буффер данных для отправки клиенту
//-----------------------------------------------------------------------------
void ClientDelegate::setOutputBuffer(QByteArray buf)
{
    engine_->setOutputBuffer(buf);
}



//-----------------------------------------------------------------------------
// Отправить результат авторизации
//-----------------------------------------------------------------------------
void ClientDelegate::sendAuthorizationResponse(ATcp::AuthResponse resp)
{
    switch (resp)
    {

    // В случае следующих кодов авторизации
    case ATcp::ar_AUTHORIZED:
    case ATcp::ar_NAME_DUPLICATE:
    case ATcp::ar_UNKNOWN_NAME:
    {
        // Создаём массив байт из полученной команды
        QByteArray arr(reinterpret_cast<const char*>(&resp),
                       sizeof(ATcp::AuthResponse));
        socket_->write(arr);
        socket_->flush();
        break;
    }

    default:
        break;
    }
}



//-----------------------------------------------------------------------------
// Отправить данные клиенту
//-----------------------------------------------------------------------------
void ClientDelegate::sendDataToTcpClient()
{
    if (socket_->isOpen())
    {
        // Пишем в сокет данные подготовленные механизмом данных
        socket_->write(engine_->getPreparedData());
        socket_->flush();
    }
}
